﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
using System.Data;
using System.Data.Common;

namespace MicroDal.Providers
{
    internal class OleDbResource : IDbResource
    {
        private OleDbConnection _connection;

        public OleDbResource(string connectionString)
        {
            _connection = new OleDbConnection(connectionString);
        }

        #region IDbResource Members
        public object ExecuteScalar(string sql, System.Data.CommandType commandType, IEnumerable<DbParameter> parameters)
        {
            using (var command = new OleDbCommand(sql, _connection))
            {
                command.CommandType = commandType;
                foreach (OleDbParameter parameter in parameters)
                    command.Parameters.Add(parameter);

                if (_connection.State != ConnectionState.Open)
                    _connection.Open();
                return command.ExecuteScalar();
            }
        }

        public int Execute(string sql, System.Data.CommandType commandType, IEnumerable<DbParameter> parameters)
        {
            using (var command = new OleDbCommand(sql, _connection))
            {
                command.CommandType = commandType;
                foreach (OleDbParameter parameter in parameters)
                    command.Parameters.Add(parameter);

                if (_connection.State != ConnectionState.Open)
                    _connection.Open();
                return command.ExecuteNonQuery();
            }
        }

        public IEnumerable<T> Fetch<T>(string sql, System.Data.CommandType commandType, IEnumerable<DbParameter> parameters, Func<System.Data.Common.DbDataReader, T> mapper)
        {
            using (var command = new OleDbCommand(sql, _connection))
            {
                command.CommandType = commandType;

                foreach (OleDbParameter parameter in parameters)
                    command.Parameters.Add(parameter);

                if (_connection.State != ConnectionState.Open)
                    _connection.Open();

                OleDbDataReader reader = command.ExecuteReader();
                if (!reader.HasRows)
                {
                    reader.Close();
                    yield break;
                }

                while (reader.Read())
                {
                    yield return mapper(reader);
                }
                reader.Close();
            }
        }

        public T FetchOne<T>(string sql, System.Data.CommandType commandType, IEnumerable<DbParameter> parameters, Func<System.Data.Common.DbDataReader, T> mapper)
        {
            using (var command = new OleDbCommand(sql, _connection))
            {
                command.CommandType = commandType;

                foreach (OleDbParameter parameter in parameters)
                    command.Parameters.Add(parameter);

                if (_connection.State != ConnectionState.Open)
                    _connection.Open();

                OleDbDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow);

                T result = reader.Read() ? mapper(reader) : default(T);

                reader.Close();
                return result;
            }
        }

        public T GetLastInsertedId<T>()
        {
            using (var command = new OleDbCommand("SELECT @@IDENTITY", _connection))
            {
                return (T)command.ExecuteScalar();
            }
        }

        public Func<ParameterConfig, object, DbParameter> ParameterInitializer
        {
            get
            {
                return (config, value) => new OleDbParameter(config.Name, value) { Direction = config.Direction };
            }
        }
        
        public string ParameterPattern { get { return @"[^\?@](?<name>[\?@]\w+)"; } }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_connection != null)
                {
                    _connection.Dispose();
                    _connection = null;
                }
            }
        }

        #endregion



        public DbTransaction GetTransaction(IsolationLevel isolationLevel)
        {
            throw new NotImplementedException("OleDb dataprovider does not support transactions.");
        }
    }
}
